/* MODULO : BMPFILE.C
*  =========================
*  Archivo : bmpfile.c
*  Modulo para manipular archivos BMP
*/

#include "bmpfile.h"

/* Definiciones para los datos del header de un archivo BMP */

#define MAGIC_NUMBER 0x4D42
#define FILE_SIZE 0X02
#define BITMAP_WIDTH 0X12
#define BITMAP_HEIGHT 0X16
#define BITSPERPIXEL 0X1C
#define IMAGE_SIZE 0X22
#define OFFSET_POSITION 0x0A
#define HEADER_SIZE 0x0E
#define COLOR_PLANES 0x1A
#define BITMAP_OFFSET 0X36
#define HORIZONTAL_RESOLUTION 0X26
#define VERTICAL_RESULOTUCION 0x2A
#define COMPRESSION 0x1E
#define IMPORTANT_COLORS 0x32
#define NUMBER_OF_PALETTE_COLORS 0x2E

/* Estructura bmpInfo
 * ==================
 * Guarda toda la informacion del header del archivo BMP, asi como la informacion
 * de la paleta de colores, en caso tengamos una imagen con menos de 24 bits
 * por pixel
 */
typedef struct bmpInfo{
	int colorPlanes;
	int headerSize;
	int height;
	int width;
	int colorDepth;
	int verticalResolution;
	int horizontalResolution;
	int offset;
	int fileSize;
	int imageSize;
	int compression;
	int importantColors;
	int numberOfPaletteColors;
	char * paletteInfo;
} bmpInfo;


void showBMPInfo(bmpInfoType bmpInfo);

/*
*  IMPLEMENTACION DE LAS FUNCIONES PUBLICAS
*  ========================================
*/
int
readBMPInfo(FILE *file, bmpInfoType *resp)
{
	bmpInfoType info;	
	int magicNumber = 0;

	if ( (info = malloc(sizeof(struct bmpInfo))) == NULL )
		return -1;
	info->colorDepth = 0;
	fread(&magicNumber, 2, 1, file);
	
	if (magicNumber != MAGIC_NUMBER )
		return 1;

	fseek(file, FILE_SIZE, SEEK_SET);
	fread(&(info->fileSize), 4, 1, file);

	fseek(file, HEADER_SIZE, SEEK_SET);
	fread(&(info->headerSize), 4, 1, file);

	fseek(file, BITMAP_WIDTH, SEEK_SET);
	fread(&(info->width), 4, 1, file);

	fread(&(info->height), 4, 1, file);

	fseek(file, BITSPERPIXEL, SEEK_SET);
	fread(&(info->colorDepth), 2, 1, file);

	fseek(file, IMAGE_SIZE, SEEK_SET);
	fread(&(info->imageSize), 4, 1, file);

	fseek(file, COLOR_PLANES, SEEK_SET);
	fread(&(info->colorPlanes), 2, 1, file);

	fseek(file, HORIZONTAL_RESOLUTION, SEEK_SET);
	fread(&(info->horizontalResolution), 4, 1, file);

	fread(&(info->verticalResolution), 4, 1, file);

	fseek(file, OFFSET_POSITION, SEEK_SET);
	fread(&(info->offset), 4, 1, file);

	fseek(file, COMPRESSION, SEEK_SET);
	fread(&(info->compression), 4, 1, file);

	fseek(file, IMPORTANT_COLORS, SEEK_SET);
	fread(&(info->importantColors), 4, 1, file);

	fseek(file, NUMBER_OF_PALETTE_COLORS, SEEK_SET);
	fread(&(info->numberOfPaletteColors), 4, 1, file);

	int paletteInfoSize;
	info->paletteInfo = NULL;
	if( info->colorDepth < 24 )
	{
	  paletteInfoSize = info->offset - HEADER_SIZE;
	  if( (info->paletteInfo = malloc(paletteInfoSize)) == NULL )
	    return 2;
	  fseek(file, BITMAP_OFFSET, SEEK_SET);
	  fread( info->paletteInfo, 1, paletteInfoSize, file );
    }

	*resp=info;
	//showBMPInfo(*resp);

	fseek(file, info->offset, SEEK_SET);

	return 0;
}



int 
writeBMPInfo(FILE *file, bmpInfoType bmpInfo)
{
	int magicNumber = MAGIC_NUMBER;
	
	fwrite(&magicNumber, 4, 1, file);

	fseek(file, FILE_SIZE, SEEK_SET);
	fwrite(&(bmpInfo->fileSize), 4, 1, file);

	fseek(file, HEADER_SIZE, SEEK_SET);
	fwrite(&(bmpInfo->headerSize), 4, 1, file);

	fseek(file, BITMAP_WIDTH, SEEK_SET);
	fwrite(&(bmpInfo->width), 4, 1, file);
	fwrite(&(bmpInfo->height), 4, 1, file);

	fseek(file, BITSPERPIXEL, SEEK_SET);
	fwrite(&(bmpInfo->colorDepth), 2, 1, file);
	
	fseek(file, COLOR_PLANES, SEEK_SET);
	fwrite(&(bmpInfo->colorPlanes), 2, 1, file);

	fseek(file, IMAGE_SIZE, SEEK_SET);
	fwrite(&(bmpInfo->imageSize), 4, 1, file);

	fseek(file, OFFSET_POSITION, SEEK_SET);
	fwrite(&(bmpInfo->offset), 4, 1, file);

	fseek(file, HORIZONTAL_RESOLUTION, SEEK_SET);
	fwrite(&(bmpInfo->horizontalResolution), 4, 1, file);

	fwrite(&(bmpInfo->verticalResolution), 4, 1, file);

	fseek(file, COMPRESSION, SEEK_SET);
	fwrite(&(bmpInfo->compression), 4, 1, file);

	fseek(file, IMPORTANT_COLORS, SEEK_SET);
	fwrite(&(bmpInfo->importantColors), 4, 1, file);

	fseek(file, NUMBER_OF_PALETTE_COLORS, SEEK_SET);
	fwrite(&(bmpInfo->numberOfPaletteColors), 4, 1, file);

	int paletteInfoSize;
	if( bmpInfo->paletteInfo != NULL )
	{
	    paletteInfoSize = bmpInfo->offset - HEADER_SIZE;
	    fseek(file, BITMAP_OFFSET, SEEK_SET);
	    if( fwrite( bmpInfo->paletteInfo, 1, paletteInfoSize, file ) < paletteInfoSize )
	    {
	      printf( "WriteBMPInfo(): error al escribir\n");
	      return 3;
	    }
	    free( bmpInfo->paletteInfo);
	    bmpInfo->paletteInfo = NULL;
    }

	//showBMPInfo(bmpInfo);

	fseek(file, bmpInfo->offset, SEEK_SET);

	return 0;
}

int
readBMPData2 (char *data, int imageSize, FILE *input)
{
	int n;
	if ( data == NULL || input == NULL ||imageSize < 0 )
		return -1;
	
	if( (n=fread(data, 1, imageSize, input)) < 0 )
		return -2;

	return 0;
}


int
writeBMPData2 (char *data, int imageSize, FILE *output)
{
	int n;
	if ( data == NULL || output == NULL ||imageSize < 0 )
		return -1;

	if( (n=fwrite(data, 1, imageSize, output)) < 0 )
		return -2;

	return 0;
}

int
getHeight(bmpInfoType bmpInfo)
{
	return bmpInfo->height;
}

int 
setHeight(bmpInfoType bmpInfo, int height)
{
	bmpInfo->height = height;
	return 0;
}

int
getWidth(bmpInfoType bmpInfo)
{
	return bmpInfo->width;
}

int 
setWidth(bmpInfoType bmpInfo, int width)
{
	bmpInfo->width = width;
	return 0;
}


int
getOffset(bmpInfoType bmpInfo)
{
	return bmpInfo->offset;
}


int
getImageSize( bmpInfoType bmpInfo )
{
	return bmpInfo->imageSize;
}

int
getImageCompression( bmpInfoType bmpInfo )
{
	return bmpInfo->compression;
}

/* Para debug */
void showBMPInfo(bmpInfoType bmpInfo)
{
	printf("\theaderSize = %d\n", bmpInfo->headerSize);
	printf("\theight = %d\n", bmpInfo->height);
	printf("\twidth = %d\n", bmpInfo->width);
	printf("\tcolorDepth = %d\n", bmpInfo->colorDepth);
	printf("\tverticalResolution= %d\n", bmpInfo->verticalResolution);
	printf("\thorizontalResolution = %d\n", bmpInfo->horizontalResolution);
	printf("\toffset= %d\n", bmpInfo->offset);
	printf("\tfileSize= %d\n", bmpInfo->fileSize);
	printf("\timageSize= %d\n", bmpInfo->imageSize);
	printf("\tcompresion= %d\n", bmpInfo->compression);	
	return;	
}
